package net.frenopatico.citadels.app;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.Principal;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.Synchronized;
import lombok.extern.java.Log;
import net.frenopatico.citadels.controller.IPlayersService;
import net.frenopatico.citadels.enums.LoginStatus;
import net.frenopatico.citadels.view.accessors.Logs;
import net.frenopatico.citadels.view.windows.LoginWindow;
import net.frenopatico.citadels.view.windows.MainWindow;

import com.vaadin.Application;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.terminal.gwt.server.HttpServletRequestListener;

/**
 * Main application in the game.
 *
 * @author eduardo.ramirez.ronco@gmail.com
 */
@Log
public class CitadelsApplication extends Application implements HttpServletRequestListener {
    private static final long serialVersionUID = -3619396575651214788L;
    private static final String AJAX_PREFIX = "UIDL";

    private static MainWindow mainWin;
    private static LoginWindow loginWin;

    private static IPlayersService playersSrv;

    private LoginStatus loginStatus;

    /**
     * Wire and initialize the application.
     *
     */
    @Inject
    public CitadelsApplication( final MainWindow mainWindow, final LoginWindow loginWindow, final IPlayersService playersService ) {
        mainWin = mainWindow;
        loginWin = loginWindow;
        playersSrv = playersService;

        setMainWindow( mainWin );
        addWindow( loginWin );
    }

    @Override
    public void init() {
        // UI initialization is deferred because we need to check the login status.
    }

    public final void onRequestStart( final HttpServletRequest request, final HttpServletResponse response ) {
        final String pathInfo = request.getPathInfo();

        // We don't manage the UI on AJAX requests.
        if( !pathInfo.contains( AJAX_PREFIX ) ) {
            final Principal userPrincipal = request.getUserPrincipal();
            final LoginStatus newStatus = checkLoginStatus( userPrincipal );

            // If this is the first access or there's a change in the login status we repaint the UI.
            if( loginStatus == null || newStatus != this.loginStatus ) {
                loginStatus = newStatus;
                final String requestURI = request.getRequestURI();
                final StringBuffer requestURL = request.getRequestURL();

                buildUI( requestURI, requestURL );
            }
        }
    }

    public void onRequestEnd( final HttpServletRequest request, final HttpServletResponse response ) {
        // There's nothing to do.
    }

    /**
     * Build the UI depending on the Login status.
     *
     * @param requestURI Main URI for the application.
     * @param requestURL Request URL to construct.
     */
    @Synchronized
    private void buildUI( final String requestURI, final StringBuffer requestURL ) {
        log.info( Logs.get( "app.buildiu.init" ) );

        switch( loginStatus ) {
            case Login:
                mainWin.initialize( requestURI );
                break;
            case Logout:
            case Unlogged:
                if( !loginWin.isInitialized() ) {
                    loginWin.initialize( requestURI );
                }

                URI reportURI;
                try {
                    reportURI = loginWin.getURL().toURI();
                    URL windowURL = new URI( reportURI.getScheme(), reportURI.getUserInfo(), reportURI.getHost(), reportURI.getPort(), reportURI.getPath(), "", null ).toURL();
                    mainWin.open( new ExternalResource( windowURL ) );
                } catch( URISyntaxException e ) {
                    log.severe( Logs.get( "app.buildui.uri.syntax" ) );
                } catch( MalformedURLException e ) {
                    log.severe( Logs.get( "app.buildui.uri.malformed" ) );
                }

                break;
            default:
                break;
        }
    }

    /**
     * Checks the login status of the user.
     *
     * @param userPrincipal Main user logged in the application.
     * @return Login status of the user.
     */
    private LoginStatus checkLoginStatus( final Principal userPrincipal ) {
        LoginStatus status;

        final Object user = getUser();
        if( userPrincipal == null && user == null ) {
            status = LoginStatus.Unlogged;
        } else if( userPrincipal == null && user != null ) {
            setUser( null );
            status = LoginStatus.Logout;
        } else if( userPrincipal != null && user == null ) {
            setUser( userPrincipal.getName() );
            status = LoginStatus.Login;
        } else {
            status = LoginStatus.Loggedin;
        }

        return status;
    }

}
